//This is the main application class.  It extends the Application main class.

#ifndef _GAME_H_
#define _GAME_H_

#include "../Helix/View/OpenGL/OpenGLSupport.h"
#include "../Helix/Application.h"
#include "../Helix/Window.h"
#include "../Helix/Renderer.h"
#include "../Helix/Updater.h"
#include "../Helix/Input.h"
#include "../Helix/Domain/HelixImage.h"
#include "../Helix/Domain/HelixMesh.h"
#include "../Helix/View/Texture.h"
#include "../Helix/View/OpenGL/OpenGLTexture2D.h"
#include "../Helix/View/RenderHelper.h"
#include "../Helix/View/OpenGL/Shader/GLSLShader.h"
#include "../Helix/Util/FileUtil.h"
#include "../Helix/Util/StringUtil.h"

#include "../Helix/Domain/Phys/Physics.h"
#include "../Helix/Domain/Phys/ODEPhysics.h"

#include "../Helix/Log.h"

#include "../Helix/Math/Constants.h"

#include <math.h>
#include <string>

using namespace std;

using namespace helix;

//Implements HRenderer and HUpdater
class Game : public Renderer, public Updater
{
private:

	static const int NUM_BOXES = 400;
	static const int NUM_ROWS = 40;

	Mesh *floor;
	Mesh *box;

	Physics *physics;
	PhysObject *base;
	PhysObject *ground;
	PhysObject *objects[NUM_BOXES];
    
	OpenGLTexture2D *cubeTexture;

	RenderHelper *openGLHelper;

	Input *input;

	GLSLShader *shader;

	float rotX;
	float rotY;
	float zoom;

public:

	Game()
	{
		
	}

	~Game()
	{
		delete cubeTexture;
		MeshUtil::deleteMesh(floor);
		MeshUtil::deleteMesh(box);
		//Do not delete openGLHelper because it will be deleted by the application when the app is shut down

		physics->deletePhysObject(base);
		physics->deletePhysObject(ground);
		
		for(int i=0;i<NUM_BOXES;i++){
			physics->deletePhysObject(objects[i]);
		}

		delete shader;

		delete physics;
	}

	virtual void renderScene();
	virtual void update(double dSeconds);

	void resetSimulation()
	{
		float dist = TWOPI / NUM_ROWS;
		for(int i=0;i<NUM_BOXES;i++){

			float x = 5.0 * cos(dist * i);
			float z = 5.0 * sin(dist * i);

			objects[i]->setPosition(Vector(x,1.1f*i,z));
			objects[i]->setVelocity(Vector(0.0,5.0,0.0));
		}

	}

	void initialize()
	{
		physics = new ODEPhysics(Vector(-100.0,-100.0,-100.0),Vector(100.0,100.0,100.0));

		PlaneRepresentation groundRep(0.0,1.0,0.0,-5.0);
		ground = physics->createPhysObject(&groundRep, true);

		BoxRepresentation rep(0.0, 10.0, 10.0, 10.0);
		BoxRepresentation boxRep(1.0,1.05,1.05,1.05);
		base = physics->createPhysObject(&rep,true);
		base->setPosition(Vector(0.0,-8.0,0.0));

		
		for(int i=0;i<NUM_BOXES;i++){
			objects[i] = physics->createPhysObject(&boxRep,false);
		}

		Application::getInstance()->logger().Println("Number of Physics Objects: %d",dSpaceGetNumGeoms(((ODEPhysics*)physics)->getSpaceId()));

		resetSimulation();

		TextureParameters tParams = TextureParameters(TextureParameters::TEXTURE_2D);
		tParams.setMinFilter(TextureParameters::TRILINEAR_SAMPLE_FILTERING);
		tParams.setMaxFilter(TextureParameters::POINT_SAMPLE_FILTERING);
		tParams.setMipMapType(TextureParameters::AUTO_GENERATE_MIP_MAPS);
		tParams.setURepeat(TextureParameters::REPEAT);
		tParams.setVRepeat(TextureParameters::REPEAT);

		//Load the texture
		Image *image= ImageFactory::loadImage("Data/Textures/cube_side.png");
		cubeTexture = new OpenGLTexture2D(image,&tParams);
		ImageFactory::deleteImage(image);

		input = Application::getInstance()->input();

		rotX = 0.0;
		rotY = 0.0;

		zoom = -20.0;

		floor = MeshUtil::loadFromHMF("Data/Meshes/cube-24-verts.hmf");
		MeshUtil::scaleVertices(floor,5.0);

		box = MeshUtil::loadFromHMF("Data/Meshes/cube-24-verts.hmf");
		MeshUtil::scaleVertices(box,0.5);

		openGLHelper = Application::getInstance()->window()->getRenderHelper();


		TextFileContent vertexShaderSource("Data/Shaders/per-pixel.vert");
		TextFileContent fragmentShaderSource("Data/Shaders/per-pixel.frag");
		shader = new GLSLShader(vertexShaderSource.getContent(),fragmentShaderSource.getContent());
		shader->useShader(true);
		float color[3] = {1.0f,1.0f,0.5f};
		shader->assignUniformF("colors",3,color);
		shader->assignUniformF("colors",3,color);

		shader->assignTextureSampler("tex0",0);

		//Set up our one-time opengl stuff
		glEnable(GL_DEPTH_TEST);			//Enable depth testing (faces hidden behind others will not be drawn)
		glClearColor(0.0,0.0,0.0,1.0);		//Set the clear color (color of the background)
		glEnable(GL_TEXTURE_2D);			//Enable texturing
		glBindTexture(GL_TEXTURE_2D,cubeTexture->getId());	//Set our texture as the currently bound one
	}
};

#endif
